﻿using DBAccessTestFramework.Demo.DataAccess;
using DBAccessTestFramework.Demo.Domain;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using System.Linq;

namespace DBAccessTestFramework.Demo.Tests.DataAccess
{
    [TestClass]
    public class DBPatientStorageTest : DBTestSupport
    {
        private DBPatientStorage target;

        private Domain.Patient newPatient;
        private Domain.Patient updatedPatient;

        [TestInitialize]
        public void Initialize()
        {
            this.AddInput("db-patient-storage-test.xml");
            this.SetConnectionString("EMREntitiesTestFramework");

            this.ExecuteScript("delete-all-patients");

            this.target = new DBPatientStorage();

            this.newPatient = new Domain.Patient();
            this.newPatient.DOB = new DateTime(2000, 1, 2);
            this.newPatient.FirstName = "test-first-name";
            this.newPatient.LastName = "test-last-name";
            this.newPatient.SSN = "123456789";
            this.newPatient.Address = new Domain.Address
            {
                Address1 = "test-address1",
                Address2 = "test-address2",
                City = "test-city",
                State = "WI",
                Zip = "12345"
            };

            this.updatedPatient = new Domain.Patient();
            this.updatedPatient.PatientId = 1;
            this.updatedPatient.DOB = new DateTime(2000, 1, 3);
            this.updatedPatient.FirstName = "test-first-name-u";
            this.updatedPatient.LastName = "test-last-name-u";
            this.updatedPatient.SSN = "123456780";
            this.updatedPatient.Address = new Domain.Address
            {
                Address1 = "test-address1-u",
                Address2 = "test-address2-u",
                City = "test-city-u",
                State = "IL",
                Zip = "12346"
            };

            this.Lock();
        }

        [TestCleanup]
        public void Cleanup()
        {
            this.Unlock();
        }

        [TestMethod]
        public void AddPatient_GivenANewPatient_RecordMustBeInserted()
        {
            this.target.AddPatient(this.newPatient);

            this.AssertDataUsingInputFile("SSN");
        }

        [TestMethod]
        public void AddPatient_GivenANewPatient_PatientIdMustBeReturned()
        {
            var actual = this.target.AddPatient(this.newPatient);

            this.AssertDataUsingCommand(
                "PatientId", 
                string.Format("<data><row PatientId='{0}'/></data>", actual),
                "SELECT PatientId FROM [dbo].[Patient] WHERE SSN = '123456789';");
        }

        [TestMethod]
        public void UpdatePatient_GivenAnExistentPatient_RecordMustBeUpdated()
        {
            this.LoadTable("new-patient");

            this.target.UpdatePatient(this.updatedPatient);

            this.AssertDataUsingInputFile("PatientId");
        }

        [TestMethod]
        public void UpdatePatient_GivenAnExistentPatient_MethodMustReturnTrue()
        {
            this.LoadTable("new-patient");

            var actual = this.target.UpdatePatient(this.updatedPatient);

            Assert.IsTrue(actual);
        }

        [TestMethod]
        public void UpdatePatient_GivenAPatientThatDoesNotExist_MethodMustReturnFalse()
        {
            var actual = this.target.UpdatePatient(this.updatedPatient);

            Assert.IsFalse(actual);
        }

        [TestMethod]
        public void FindPatients_GivengAFilterWithAFirstNameValue_PatientsWithMatchingFirstNameMustBeReturned()
        {
            this.LoadTable("several-patients");

            var actual = this.target.FindPatients(new FindPatientFilter { FirstName = "test-first-name1" });

            Assert.AreEqual(2, actual.Count);

            PatientItem actualPatientItem1 = actual.FirstOrDefault(a => a.PersonId == 1);
            PatientItem actualPatientItem11 = actual.FirstOrDefault(a => a.PersonId == 11);

            PatientItem expectedPatientItem1 = new PatientItem
            {
                DOB = new DateTime(2000, 1, 1),
                FirstName = "test-first-name1",
                LastName = "test-last-name1",
                PersonId = 1
            };

            PatientItem expectedPatientItem11 = new PatientItem
            {
                DOB = new DateTime(2000, 1, 11),
                FirstName = "test-first-name11",
                LastName = "test-last-name11",
                PersonId = 11
            };

            this.ComparePatientItem(expectedPatientItem1, actualPatientItem1);
            this.ComparePatientItem(expectedPatientItem11, actualPatientItem11);
        }

        [TestMethod]
        public void DeletePatient_GivenAPatientId_RecordMustBeSetAsDeleted()
        {
            this.LoadTable("several-patients");

            this.target.DeletePatient(11);

            this.AssertDataUsingInputFile("PatientId");
        }

        [TestMethod]
        public void DeletePatient_GivenAPatientId_MethodMustReturnTrue()
        {
            this.LoadTable("several-patients");

            var actual = this.target.DeletePatient(11);

            Assert.IsTrue(actual);
        }

        [TestMethod]
        public void DeletePatient_GivenAnPatientIdThatDoesNotExist_MethodMustReturnFalse()
        {
            var actual = this.target.DeletePatient(1000);

            Assert.IsFalse(actual);
        }

        private void ComparePatientItem(PatientItem expected, PatientItem actual)
        {
            Assert.IsNotNull(actual, "Patient with id '{0}' not found.", expected.PersonId);
            
            Assert.AreEqual(expected.PersonId, actual.PersonId);
            Assert.AreEqual(expected.DOB, actual.DOB);
            Assert.AreEqual(expected.FirstName, actual.FirstName);
            Assert.AreEqual(expected.LastName, actual.LastName);
        }
    }
}
